#include "vmLibrary.h"
#include "xMappingNode.h"
#include "xNodeType.h"
#include "xInMappingPort.h"
#include "xOutMappingPort.h"
#include "xlink.h"

int xMappingNode::xMappingNodeNumber = 1 ;

xMappingNode::xMappingNode(xNodeType *p) {
	_serial_no = xMappingNodeNumber ; xMappingNodeNumber++ ;
	_level_id = -1 ;
	_nodetype = p ;
	_composite_parent = NULL ;
	_isForeachElements = false;
	_isUnfold = false;
	_MDSFlag = false;
	p->setParent(this);   // set this mapping node as the parent of xNodeType 
	p->setupPorts();
}
xMappingNode::xMappingNode() {
	_serial_no = xMappingNodeNumber ; xMappingNodeNumber++ ;
	_level_id = -1 ;
	_nodetype = NULL ;
	_composite_parent = NULL ;
}
/*
xMappingNode::xMappingNode(vmdescriptor *vd) {
	_level_id = -1 ;
	_nodetype = (xNodeType *) new xNodeType_VM(this, vd->id);
	for (int i = 0 ; i < vd->para_no ; i++ ) {
		xInMappingPort *p = new xInMappingPort(vd->para_name[i],vd->para_type[i], this) ;
		_inports.push_back(p) ;
	}
	_outport = new xOutMappingPort("", this);
}
*/
xMappingNode::~xMappingNode() {
	clear_ports();
	_composite_parent = NULL ;
}

void xMappingNode::clean_nodetype() {
	clear_ports();
	_nodetype = NULL ;
}
void xMappingNode::clear_ports() {
	for (unsigned int i=0; i< _inports.size(); i++) {
		xInMappingPort *p = _inports.back();
		_inports.pop_back();
		delete p ;
	}
	for (unsigned int i=0; i< _outports.size(); i++) {
		xOutMappingPort *p = _outports.back();
		_outports.pop_back();
		delete p ;
	}
	delete _nodetype ;
	_nodetype = NULL ;
}

void xMappingNode::apply_nodetype(xNodeType *nt) {
	if (_nodetype != NULL)
		clean_nodetype() ;
	_nodetype = nt ;
	nt->setParent(this);
	nt->setupPorts();
}
xInMappingPort * xMappingNode::getInPort(std::string pname) const {
	for (auto itr = _inports.begin(); itr != _inports.end(); itr++) {
		xInMappingPort * inp = (*itr);
		if (inp->getName() == pname) {
			return inp ;
		}
	}
	assert(false); // at currernt progress, most of times, the pname has typo errors so this assert is caused.
	return NULL ; 
	
}
int xMappingNode::getInportIndex(xInMappingPort *p) {

	for (unsigned int i=0; i< _inports.size(); i++) {
		xInMappingPort * inp = _inports[i];
		if (inp == p) return i ;
	}
	assert(false);
	return -1 ;
}
int xMappingNode::getOutportIndex(xOutMappingPort *p) {

	for (unsigned int i=0; i< _outports.size(); i++) {
		xOutMappingPort * op = _outports[i];
		if (op == p) return i ;
	}
	assert(false);
	return -1 ;
}
xOutMappingPort * xMappingNode::getOutPort(std::string pname) {
	vector<xOutMappingPort *>::iterator itr ;
	for (itr = _outports.begin(); itr != _outports.end(); itr++) {
		xOutMappingPort * op = (*itr);
		if (op->getName() == pname) {
			return op ;
		}
	}
	assert(false);
	return NULL ;
}
xInMappingPort * xMappingNode::newInPort(string name, string type, MNClass para_class, string defaultvalue, MNCategory category) {

	assert(_nodetype != NULL) ;
	if (_nodetype->getInportFixed()) {
		assert(false);  // the inport of this mapping node is fixed and new inports cannot be added
		                // the problem should be addressed in mapping dialog
		return NULL ; 
	}
	xInMappingPort *p = new xInMappingPort( this, name, type, para_class, defaultvalue, category);
	_inports.push_back(p) ;
	return p ;
}

xOutMappingPort * xMappingNode::newOutPort( string name, string type, MNClass para_class, string defaultvalue, MNCategory category ){
	
	assert(_nodetype != NULL) ;
	if (_nodetype->getOutportFixed()) {
		assert(false);  // the inport of this mapping node is fixed and new outports cannot be added
		// the problem should be addressed in mapping dialog
		return NULL ; 
	}
	xOutMappingPort *p = new xOutMappingPort( this, name, type, para_class, defaultvalue, category);
	_outports.push_back(p) ;
	return p ;
}

vector<xMappingNode*>  xMappingNode::getParentNodes() {
	vector<xOutMappingPort*>::iterator itr ;
	vector<xMappingNode *> ret ;

	for (itr = _outports.begin(); itr != _outports.end(); itr++) {
		xOutMappingPort *outp = (*itr);
		vector<xLink *> links = outp->getLinks();
		for (unsigned int i=0;i< links.size() ; i++) {	
			xLink *lnk = links[i];
			xMappingNode  *parentnode  = lnk->right->getParent();
			assert(parentnode != NULL);
			if(ret.size() == 1 && ret[0] == parentnode){
				continue;
			}
			ret.push_back(parentnode) ;
		}
	}
	return ret ;
}
vector<xMappingNode*>  xMappingNode::getChildNodes() {
	vector<xMappingNode *> ret ;
	int in_sz  = this->getInportSize();

	for (int i=0;i< in_sz ; i++) {
		xInMappingPort * inp = this->getInPort(i);
		assert(inp != NULL);

		xLink *lnk = inp->getLink();
		if (lnk != NULL) {
			xMappingNode *kidnode = lnk->left->getParent();
			assert(kidnode != NULL);
			ret.push_back(kidnode) ;
		}
	}
	return ret ;
}

void xMappingNode::deletePort(int i) {
	vector<xInMappingPort *>::iterator itr ;
	xInMappingPort *p = _inports[i] ;
	itr = find(_inports.begin(), _inports.end(), p) ;
	_inports.erase(itr);
	delete p ;
}

void xMappingNode::deleteUnfoldedChild( xMappingNode * nd ) {
	auto it = std::find( this->_unfoldChildren.begin(), this->_unfoldChildren.end(), nd );
	if( it != this->_unfoldChildren.end() ) {
		this->_unfoldChildren.erase( it );
	}
}

void xMappingNode::addUnfoldChild( xMappingNode* child )
{
	this->_unfoldChildren.push_back(child);
}

std::vector<xMappingNode*>& xMappingNode::getUnfoldChildren()
{
	return this->_unfoldChildren;
}

std::string xMappingNode::toMDString( xNodeType::MDSType type )
{
	ostringstream MDSstr ;
	MDSstr << _nodetype->toMDString(type);

	if ( _isUnfold ){
		MDSstr << " {" << endl;

		std::vector<xMappingNode*>::iterator itr;
		for ( itr = _unfoldChildren.begin(); itr != _unfoldChildren.end(); ++itr )
		{
			MDSstr << (*itr)->toMDString(type);
		}
		MDSstr << " }" << endl;
	} 

	this->_MDSFlag = true;

	string mds = MDSstr.str();
	return mds;
}

void xMappingNode::setMDSFlag( bool b )
{
	this->_MDSFlag = b;
}

bool xMappingNode::getMDSFlag()
{
	return this->_MDSFlag;
}

/*
void xMappingNode::apply_vm(vmdescriptor *vd) {
	clear_ports() ; // clear all the ports and nodetype 

	_nodetype = (xNodeType*) new xNodeType_VM(this, vd->id);
	_level_id = -1 ;
	for (int i = 0 ; i < vd->para_no ; i++ ) {
		xInMappingPort *p = new xInMappingPort(vd->para_name[i], vd->para_type[i], this) ;
		_inports.push_back(p) ;
	}
	_outport = new xOutMappingPort("", this);
}
*/

string xMappingNode::getType() {
	return _nodetype->getType();
}

xInMappingPort * xMappingNode::getInPort(unsigned int i) const {
	auto p = this->_inports.size();
	assert(i < _inports.size()) ;
	return _inports[i];
}
